/*
 * @author Chris
 * @since 2023/1/23 15:04
 *
 * @brief    The project will simulate UART use P3.0/P3.1 GPIO.
 *
 * @note     {@link ConfigUART()} config UART baud.
 *           {@link StartRXD()} begin to receive.
 *           {@link StartTXD()} begin to send.
 *
 * @warning  The result is error.
 *
 * Change Logs:
 *   Date           Author       Notes
 *   2023-01-23     Chris        the first version
 */

#include "reg51.h"

#define PIN_RXD P3_0
#define PIN_TXD P3_1

__bit RxdOrTxd = 0; // 当前状态是接收还是发送
__bit RxdEnd = 0;   // 接收完成标志
__bit TxdEnd = 0;   // 发送完成标志
unsigned char RxdBuf = 0;
unsigned char TxdBuf = 0;

void ConfigUART(unsigned int baud);

void StartRxd();

void StartTxd(unsigned char dat);

int main() {

    EA = 1;
    ConfigUART(9600);

    while (1) {
        while (PIN_RXD);   // 发送起始位时拉低
        StartRxd();
        while (!RxdEnd);
        StartTxd(RxdBuf + 1);
        while (!TxdEnd);
    }
}

void ConfigUART(unsigned int baud) {
    TMOD &= 0xF0;
    TMOD |= 0x02;
    TH0 = 256 - (11059200 / 12) / baud;
}

void StartRxd() {
    TL0 = 256 - ((256 - TH0) >> 1);
    ET0 = 1;
    TR0 = 1;
    RxdEnd = 0;
    RxdOrTxd = 0;
}

void StartTxd(unsigned char dat) {
    TxdBuf = dat;
    TL0 = TH0;
    ET0 = 1;
    TR0 = 1;        // 定时器 T0 中断
    PIN_TXD = 0;    // 发送起始位
    TxdEnd = 0;
    RxdOrTxd = 1;
}

/**
 * 定时器 T0 中断服务函数
 */
void InterruptTimer0() __interrupt(1) {
    static unsigned char cnt = 0;

    // 发送
    if (RxdOrTxd) {
        cnt++;
        if (cnt <= 8) {
            // 低位在先依次发送数据
            PIN_TXD = TxdBuf & 0x01;
            TxdBuf >>= 1;
        } else if (cnt == 9) {
            // 发送结束位
            PIN_TXD = 1;
        } else {
            // 发送结束
            cnt = 0;
            TR0 = 0;
            TxdEnd = 1;
        }
        return;
    }

    // 接收
    if (cnt == 0) {
        // 接收起始位
        if (!PIN_RXD) {
            RxdBuf = 0;
            cnt++;
        } else {
            // 起始位不为 0 时，中止接收。关闭 TR0
            TR0 = 0;
        }
    } else if (cnt <= 8) {
        RxdBuf = (RxdBuf >> 1) | (PIN_RXD << 7);
        cnt++;
    } else {
        cnt = 0;
        TR0 = 0;
        if (PIN_RXD) {
            RxdEnd = 1;
        }
    }
}

